package ws

import (
	"context"
	"encoding/json"
	"log"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/olahol/melody"
	"gorm.io/gorm"

	"chat-app/internal/app/models" // 替换为您的实际包路径
)

type Manager struct {
	melody    *melody.Melody
	clients   map[uint]*melody.Session // 使用 uint 匹配 User.ID
	clientsMu sync.RWMutex
	db        *gorm.DB // 数据库实例
}

func NewManager(m *melody.Melody, db *gorm.DB) *Manager {
	mgr := &Manager{
		melody:  m,
		clients: make(map[uint]*melody.Session),
		db:      db,
	}

	m.HandleConnect(mgr.handleConnect)
	m.HandleMessage(mgr.handleMessage)
	m.HandleClose(mgr.handleClose)
	m.HandleError(mgr.handleError)

	return mgr
}

func (m *Manager) HandleRequest(c *gin.Context) {

	userID, exists := c.Get("userID")
	if !exists {
		c.AbortWithStatusJSON(401, gin.H{"error": "未认证用户"})
	}

	uid, ok := userID.(uint)
	if !ok {
		c.AbortWithStatusJSON(401, gin.H{"error": "无效用户ID格式"})
		return
	}

	// 3. 将用户ID设置到原始请求的上下文中
	req := c.Request.WithContext(context.WithValue(c.Request.Context(), "userID", uid))
	c.Request = req

	// 4. 继续处理WebSocket升级
	err := m.melody.HandleRequest(c.Writer, c.Request)
	if err != nil {
		log.Printf("WebSocket升级错误: %v", err)
		c.AbortWithStatus(500)
	}
}

func (m *Manager) handleConnect(s *melody.Session) {
	// 从原始请求的上下文中获取用户ID
	ctx := s.Request.Context()
	userID := ctx.Value("userID")
	if userID == nil {
		log.Printf("认证信息中缺少用户ID")
		_ = s.CloseWithMsg(melody.FormatCloseMessage(4000, "认证失败"))
		return
	}

	uid, ok := userID.(uint)
	if !ok {
		log.Printf("无效的用户ID类型")
		_ = s.CloseWithMsg(melody.FormatCloseMessage(4000, "无效的用户ID"))
		return
	}

	m.clientsMu.Lock()
	defer m.clientsMu.Unlock()

	// 7. 允许多设备登录：只断开长时间不活动的旧会话
	if oldSession, ok := m.clients[uid]; ok {
		value, exists := oldSession.Get("lastActive")
		if exists {
			if lastActive, ok := value.(time.Time); ok {
				if time.Since(lastActive) > 5*time.Minute {
					_ = oldSession.CloseWithMsg(melody.FormatCloseMessage(4001, "新连接已建立"))
				}
			} else {
				log.Printf("用户 %d 的 lastActive 类型错误", uid)
			}
		} else {
			// 没有 lastActive，直接关闭旧会话
			_ = oldSession.CloseWithMsg(melody.FormatCloseMessage(4001, "新连接已建立"))
		}
	}

	// 8. 设置新会话的属性
	s.Set("userID", uid)
	s.Set("lastActive", time.Now()) // 初始化最后活动时间

	m.clients[uid] = s

	log.Printf("用户 %d 连接成功", uid)
}

func (m *Manager) handleMessage(s *melody.Session, msg []byte) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// 更新最后活动时间
	s.Set("lastActive", time.Now())

	// 解析为 models.Message
	var message models.Message
	if err := json.Unmarshal(msg, &message); err != nil {
		log.Printf("消息解析错误: %v", err)
		return
	}

	// 设置发送者ID
	senderID := s.MustGet("userID").(uint)
	message.SenderID = senderID
	message.CreatedAt = time.Now()

	switch message.MessageType {
	case models.MessageTypePrivate:
		m.sendPrivateMessage(ctx, message)
	case models.MessageTypeGroup:
		m.sendGroupMessage(ctx, message)
	default:
		log.Printf("未知消息类型: %s", message.MessageType)
	}
}

func (m *Manager) sendPrivateMessage(ctx context.Context, msg models.Message) {
	// 验证好友关系
	if !m.areFriends(ctx, msg.SenderID, msg.RecipientID) {
		log.Printf("用户 %d 和 %d 不是好友", msg.SenderID, msg.RecipientID)
		return
	}

	// 存储到数据库
	if err := m.db.WithContext(ctx).Create(&msg).Error; err != nil {
		log.Printf("存储私聊消息失败: %v", err)
	}

	// 尝试发送给接收方
	m.sendToUser(msg.RecipientID, msg)
}

func (m *Manager) areFriends(ctx context.Context, userID, friendID uint) bool {
	var count int64
	err := m.db.WithContext(ctx).Model(&models.Friend{}).
		Where("(user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)",
			userID, friendID,
			friendID, userID).
		Count(&count).Error

	return err == nil && count > 0
}

func (m *Manager) sendGroupMessage(ctx context.Context, msg models.Message) {
	// 验证用户是否在群中
	if !m.isGroupMember(ctx, msg.GroupID, msg.SenderID) {
		log.Printf("用户 %d 不在群组 %d 中", msg.SenderID, msg.GroupID)
		return
	}

	// 存储到数据库
	if err := m.db.WithContext(ctx).Create(&msg).Error; err != nil {
		log.Printf("存储群消息失败: %v", err)
	}

	// 获取群成员并发送
	members := m.getGroupMembers(ctx, msg.GroupID)
	for _, memberID := range members {
		if memberID != msg.SenderID {
			// 使用单独的协程发送，避免阻塞
			go m.sendToUser(memberID, msg)
		}
	}
}

func (m *Manager) isGroupMember(ctx context.Context, groupID, userID uint) bool {
	var count int64
	err := m.db.WithContext(ctx).Model(&models.GroupMember{}).
		Where("group_id = ? AND user_id = ?", groupID, userID).
		Count(&count).Error

	return err == nil && count > 0
}

func (m *Manager) getGroupMembers(ctx context.Context, groupID uint) []uint {
	var memberIDs []uint
	m.db.WithContext(ctx).Model(&models.GroupMember{}).
		Where("group_id = ?", groupID).
		Pluck("user_id", &memberIDs)
	return memberIDs
}

func (m *Manager) sendToUser(userID uint, message models.Message) {
	m.clientsMu.RLock()
	defer m.clientsMu.RUnlock()

	if session, ok := m.clients[userID]; ok {
		msgBytes, err := json.Marshal(message)
		if err != nil {
			log.Printf("消息序列化错误: %v", err)
			return
		}

		if err := session.Write(msgBytes); err != nil {
			log.Printf("发送消息给用户 %d 失败: %v", userID, err)
		} else {
			log.Printf("消息已成功发送给用户 %d", userID)
		}
	} else {
		log.Printf("用户 %d 不在线", userID)
	}
}

func (m *Manager) handleClose(s *melody.Session, code int, reason string) error {
	m.clientsMu.Lock()
	defer m.clientsMu.Unlock()

	userID, exists := s.Get("userID")
	if exists {
		uid := userID.(uint)
		log.Printf("用户 %d 断开连接, 原因: %s", uid, reason)
		delete(m.clients, uid)
	}
	return nil
}

func (m *Manager) handleError(s *melody.Session, err error) {
	log.Printf("WebSocket错误: %v", err)
}
